#ifndef SHAPE_H
#define SHAPE_H

/*!
 * \file     shape.h
 * \brief    Tetris Shape.
 * \author   Duriez Franck
 * \author   Carton Paul-Henri
 * \version  0.1
 */

#include "tetrisexception.h"
#include "squarecontainer.h"
#include "square.h"
#include "coloredsquare.h"
#include "action.h"
#include "grid.h"
#include "nextshapemanager.h"

class Grid;
class NextShapeManager;

/*!
 * \class Shape
 * \brief Tetris Shape.
 */
class Shape : public SquareContainer /*{{{*/
{
/*{{{*/
public:
	/*!
	 * \enum ShapeType
	 * \brief Form of the Shape (see setShapeType).
	 */
	enum ShapeType /*{{{*/
	{
		I, ///< Line type.
		O, ///< %Square type.
		T, ///< T type.
		L, ///< L type.
		J, ///< Reverse L type.
		S, ///< S type.
		Z  ///< Reverse S type.
	}; /*}}}*/

	#define DIR_NUMBER 4 ///< Number of Directions

	/*!
	 * \enum Direction
	 * \brief Indicate the orientation of the Shape.
	 */
	enum Direction /*{{{*/
	{
		Up    = 0,
		Left  = 1,
		Down  = 2,
		Right = 3
	}; /*}}}*/

	/*!
	 * \brief Constructor.
	 * \param displayWidget : displayer
	 * \param parent        : parent of the widget
	 * \param dt            : pointer to the infinitesimal part of time
	 */
	Shape(QSFMLWidget* displayWidget, QObject* parent, float* dt);

	/*!
	 * \brief Destructor.
	 */
	virtual ~Shape();

	/*!
	 * \brief Set the ShapeType
	 * \param atype : the type to set
	 */
	void setShapeType(ShapeType atype);

	/*!
	 * \brief Set a random ShapeType
	 */
	void setRandomShapeType();

protected:
	/*!
	 * \brief Rotate the Squares in the Container.
	 * \warning Don't confound with Shape::rotate.
	 *
	 * This function rotate the Squares in the "symbolic part" of
	 * the Shape : Container::m_objects.
	 */
	void rotateInContainer(unsigned int n = 1);

	/*!
	 * \brief Antirotate the Squares in the Container.
	 *
	 * This function antirotate the Squares in the "symbolic part" of
	 * the Shape : Container::m_objects.
	 */
	void antiRotateInContainer(unsigned int n = 1);

	/*!
	 * Allows NextShapeManager to set the good
	 * Shape::m_current_orientation before giving the Shape to the Grid
	 */
	friend class NextShapeManager;

	Direction m_current_orientation; ///< Current orientation of the shape
	bool m_initialized; ///< Is there something in the shape ?
	float m_angle_to_travel; ///< Angle to travel until stop rotate
/*}}}*/

	/*********************
	 * MOVING PERMISSION *
	 *********************/
/*{{{*/
public:
	/*!
	 * \brief Is the Shape on the left ?
	 */
	bool isOnTheLeftBoard() const;

	/*!
	 * \brief Is the Shape on the Right ?
	 */
	bool isOnTheRightBoard() const;

	/*!
	 * \brief Is the Shape down ?
	 */
	bool isOnTheDownBoard() const;

	/*!
	 * \brief Is the Shape up ?
	 */
	bool isOnTheUpBoard() const;

	/*!
	 * \brief Is there a Square on the left ?
	 */
	bool isASquareOnTheLeft() const;

	/*!
	 * \brief Is there a Square on the right ?
	 */
	bool isASquareOnTheRight() const;

	/*!
	 * \brief Is there a Square down ?
	 */
	bool isASquareDown() const;

	/*!
	 * \brief Is there a Square up ?
	 */
	bool isASquareUp() const;

	/*!
	 * \brief Is the rotation permitted ?
	 */
	bool canRotate() const;

	/*!
	 * \brief Is the antirotation permitted ?
	 */
	bool canAntiRotate() const;
/*}}}*/

	/************
	 * DRAWABLE *
	 ************/
/*{{{*/
	virtual void update();

	void draw() const;

	std::string toString() const;

	void setCenter(Vector2f center);

	void setPosition(Vector2f position);

	void setRotation(float angle);

	void moveCenter(Vector2f dcenter);

	void move(Vector2f dposition);

	/*!
	 * \brief Graphic rotation.
	 * \warning Don't confound it with Shape::rotateInContainer
	 *
	 * Rotation of the graphic part of the shape.
	 */
	void rotate(float dangle);
/*}}}*/

	/***********
	 * ACTIONS *
	 ***********/
/*{{{*/
public:
	Action<Shape> moveLeft;
	Action<Shape> moveRight;
	Action<Shape> moveUp;
	Action<Shape> moveDown;

	Action<Shape> rotateAction;
	Action<Shape> antiRotateAction;

private:
	void moveLeftStartingProcess();
	void moveLeftProcess();
	void moveLeftStoppingProcess();

	void moveRightStartingProcess();
	void moveRightProcess();
	void moveRightStoppingProcess();

	void moveUpStartingProcess();
	void moveUpProcess();
	void moveUpStoppingProcess();

	void moveDownStartingProcess();
	void moveDownProcess();
	void moveDownStoppingProcess();

	void rotateStartingProcess();
	void rotateProcess();
	void rotateStoppingProcess();

	void antiRotateStartingProcess();
	void antiRotateProcess();
	void antiRotateStoppingProcess();
/*}}}*/

	/********
	 * GRID *
	 ********/
/*{{{*/
	friend class Grid;

public:
	/*!
	 * \brief Return the position in the Grid.
	 */
	Vector2i getPositionInGrid() const;

private:
	/*!
	 * \brief Change the Grid which contains this Shape.
	 */
	void setGrid(Grid* agrid);

	/*!
	 * \brief Set the position in the Grid which contains this Shape.
	 */
	void setPositionInGrid(Vector2i coord);

	Vector2i m_position_in_grid; ///< Current position in the grid
	Grid* m_grid;                ///< Grid in which this is contained
/*}}}*/

	/***********
	 * SQUARES *
	 ***********/
/*{{{*/
protected:
	/*!
	 * \brief Give the good format to the Square at coord.
	 * \todo throw management
	 */
	void format(Vector2i coord) const throw(TetrisException::ExpectedSquare);

	/*!
	 * \brief Declare that the object at coord is dead.
	 *
	 * \todo Not already implemented, because the example Shapes don't
	 *       have auto destroying Squares.
	 */
	virtual void declareDeadObject(Vector2i) {};

	/*!
	 * \brief Square type choicer for filling the Shape.
	 * \param form  : form descriptor tabular
	 * \param num   : number of the square in the tabular
	 * \param atype : shape type
	 */
	virtual Square* mySquareType(int* form,
	                             int  num,
	                             ShapeType atype) = 0;
/*}}}*/
}; /*}}}*/

#endif // SHAPE_H
